package org.seed.rpc.clientstub;

import lombok.extern.slf4j.Slf4j;
import org.objenesis.instantiator.util.ClassUtils;
import org.seed.rpc.common.util.IconSpecialUtil;
import org.seed.rpc.core.RegistryClient;
import org.seed.rpc.core.RegistryOperator;
import org.seed.rpc.core.RpcReference;
import org.seed.rpc.core.RuntimeLocal;
import org.seed.rpc.core.etcd.EtcdClient;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;

import java.util.*;


@Slf4j
@EnableConfigurationProperties(RpcConfigProperties.class)
@Configuration
@ConditionalOnProperty("rpc.local.registry")
public class RpcClientAutoConfigure implements InstantiationAwareBeanPostProcessor {


    private RpcConfigProperties rpcServerProperties;

    private RegistryClient etcdClient;

    /**
     * 本地rpc接口是否完成扫描
     */
    private static boolean localRpcInterfaceScanned = false;

    /**
     * 接口名称映射至代理对象
     */
    private static Map<String, Object> interfaceProxyMap = new HashMap<>();

    public RpcClientAutoConfigure(@Autowired RpcConfigProperties rpcServerProperties) {
        this.rpcServerProperties = rpcServerProperties;
    }

    /**
     * 扫描本地 rpc-service 接口
     */
    public void scanLocalRpcInterfaces() {
        if (localRpcInterfaceScanned) {
            return;
        }
        log.info("scanning local rpc-service interfaces ...");
        // 1. 从注册中心下载可用服务映射表，缓存至本地
        downloadRegistries();
        log.info(IconSpecialUtil.randomIcon() + " registry config data downloaded.");
        // 2. 扫描全部带有注解 @RpcService 的接口
        log.info(IconSpecialUtil.randomIcon() + " please wait for a moment, scanning local class files ...");
        Set<String> interfaceSet = pickInterfaceRemoteCall();
        log.info(IconSpecialUtil.randomIcon() + " local interfaces referer remote-call scanned.");
        // 3. 创建代理实体，并将代理注册到spring容器
        if (interfaceSet != null && interfaceSet.size() > 0) {
            createProxyAsSpringBean(interfaceSet);
            log.info(IconSpecialUtil.randomIcon() + " remote-service proxy for each interface created.");
        }
        localRpcInterfaceScanned = true;
        log.info(IconSpecialUtil.randomIcon() + " local client rpc-service initialized success.");
    }


    private void downloadRegistries() {
        RegistryClient registryClient = RuntimeLocal.getRegistryClient();
        if (registryClient == null) {
            etcdClient = new EtcdClient(rpcServerProperties.getRegistry());
            RuntimeLocal.setRegistryClient(etcdClient);
        } else {
            etcdClient = registryClient;
        }
        Map<String, List<String>> map = RegistryOperator.getServiceEndpointMap(etcdClient);
        RuntimeLocal.cacheLocalServiceNameEndpointMap(map);
    }

    /**
     * 分拣出远程调用的rpc接口
     */
    private Set<String> pickInterfaceRemoteCall() {
        try {
            Set<String> interfaceSet = new HashSet<>();
            Set<String> concreteSet = new HashSet<>();
            if (rpcServerProperties.getBasePackageScan() == null || rpcServerProperties.getBasePackageScan().trim().equals("")) {
                interfaceSet = RpcServiceInterfaceScanner.scanInterfaceSet("*");
                concreteSet = RpcServiceInterfaceScanner.scanConcreteSet("*");
            } else {
                String basePackages = rpcServerProperties.getBasePackageScan();
                String[] basePackageArray = basePackages.split(",");
                for (String packagePath : basePackageArray) {
                    if (packagePath != null && !packagePath.trim().equals("")) {
                        interfaceSet.addAll(RpcServiceInterfaceScanner.scanInterfaceSet(packagePath));
                        concreteSet.addAll(RpcServiceInterfaceScanner.scanConcreteSet(packagePath));
                    }
                }
            }
            // 删除本地带有实体类的接口
            if (interfaceSet == null || interfaceSet.size() == 0) {
                log.info(IconSpecialUtil.randomIcon() + " not found interfaces defined by annotation `@RpcService`");
                return null;
            }
            if (concreteSet == null || concreteSet.size() == 0) {
                log.info(IconSpecialUtil.randomIcon() + " not found concrete class defined by annotation `@RpcService`");
                log.info("╔═════════════════════════════════════════════════════════►");
                for (String interfaceName : interfaceSet) {
                    log.info("║" + " ➜ " + interfaceName);
                }
                log.info("╚═════════════════════════════════════════════════════════►");
                return interfaceSet;
            }
            for (String name : concreteSet) {
                Class<?> clazz = ClassUtils.getExistingClass(this.getClass().getClassLoader(), name);
                Class[] interfaceCurrent = clazz.getInterfaces();
                if (interfaceCurrent != null && interfaceCurrent.length > 0) {
                    for (Class clazzCurrent : interfaceCurrent) {
                        interfaceSet.remove(clazzCurrent.getName());
                    }
                }
            }
            if (interfaceSet.size() > 0) {
                log.info("╔═════════════════════════════════════════════════════════►");
                for (String interfaceName : interfaceSet) {
                    log.info("║" + " ➜ " + interfaceName);
                }
                log.info("╚═════════════════════════════════════════════════════════►");
            }
            return interfaceSet;
        } catch (Exception e) {
            log.error("scan rpc-interface error: {}", e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    private void createProxyAsSpringBean(Set<String> interfaces) {
        for (String className : interfaces) {
            Class<?> interfaceClass = ClassUtils.getExistingClass(this.getClass().getClassLoader(), className);
            Object proxyObject = RpcProxy.create(interfaceClass);
            interfaceProxyMap.put(className, proxyObject);
        }
    }


    @Override
    public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException {
        scanLocalRpcInterfaces();
        ReflectionUtils.doWithFields(bean.getClass(), field -> {
            if (field.isAnnotationPresent(RpcReference.class)) {
                Class clazz = field.getType();
                if (!clazz.isInterface()) {
                    log.error("{} must be an interface type of `@RpcService`", clazz.getName());
                    return;
                }
                Object instanceProxy = interfaceProxyMap.get(clazz.getName());
                field.setAccessible(true);
                field.set(bean, instanceProxy);
                log.debug("<== settled rpc field value: {}.{}", bean.getClass().getName(), field.getName());
            }
        });
        return true;
    }


}
